package miniproj.models.reservation;

import java.text.DateFormat;
import java.util.*;

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;

import miniproj.exception.*;

/**
 * This class is a specific Date class. It allows to use a java.sql.Date object
 * with a Schedule, more adapted in this problem than a hh:mm
 * 
 * @author Baptiste Viale
 * 
 */
public class Date implements java.io.Serializable {

	private static final long serialVersionUID = -4935032642822530592L;

	@XmlJavaTypeAdapter(miniproj.services.serialization.SerializedDate.class)
	private java.sql.Date date;

	@XmlElement
	private Schedule schedule;

	/**
	 * Instantiates a new date. (JavaBeans)
	 */
	public Date() {
	}

	/**
	 * Instantiates a new date.
	 * 
	 * @param year
	 *            the year of the date
	 * @param month
	 *            the month of the date
	 * @param day
	 *            the day of the date
	 * @param schedule
	 *            the schedule
	 * @throws IncorrectDateFormatException
	 *             an incorrect date format exception if the date is invalid
	 */
	public Date(int year, int month, int day, Schedule schedule)
			throws IncorrectDateFormatException {
		month--;

		Calendar c = Calendar.getInstance();
		c.set(year, month, day, 0, 0, 0);

		// Control if the date is valid
		//
		if (!(c.get(Calendar.YEAR) == year && c.get(Calendar.MONTH) == month && c
				.get(Calendar.DATE) == day))
			throw new IncorrectDateFormatException(year, month, day);

		// Control if the date is a working day
		//
		if (c.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY
				|| c.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)
			throw new IncorrectDateFormatException(
					"The sundays and saturdays are not available for booking.");

		this.date = new java.sql.Date(c.getTimeInMillis());

		c.setTime(new java.util.Date());
		c.set(c.get(Calendar.YEAR), c.get(Calendar.MONTH),
				c.get(Calendar.DATE), 0, 0, 0);

		// Control if the date is not passed
		//
		if (this.date.before(c.getTime())) {
			throw new IncorrectDateFormatException(
					"The date is before current date");
		}

		this.schedule = schedule;
	}

	/**
	 * Instantiates a new date.
	 * 
	 * @param month
	 *            the month of the date
	 * @param day
	 *            the day of the date
	 * @param schedule
	 *            the schedule
	 * @throws IncorrectDateFormatException
	 *             an incorrect date format exception if the date is invalid
	 */
	public Date(int month, int day, Schedule schedule)
			throws IncorrectDateFormatException {
		this(Calendar.getInstance().get(Calendar.YEAR), month, day, schedule);
	}

	/**
	 * Instantiates a new date.
	 * 
	 * @param dateTimeMillis
	 *            the date time in milliseconds format
	 * @param schedule
	 *            the schedule
	 */
	public Date(long dateTimeMillis, Schedule schedule) {
		this.date = new java.sql.Date(dateTimeMillis);
		this.schedule = schedule;
	}

	/**
	 * Return the schedule of this date.
	 * 
	 * @return the schedule
	 */
	public Schedule getSchedule() {
		return this.schedule;
	}

	/**
	 * Return the java.sql.Date object of this Date (used to store the year,
	 * month and day)
	 * 
	 * @return the date
	 */
	public java.sql.Date getDate() {
		return this.date;
	}

	/**
	 * Override the equals object method for this Date object.
	 * 
	 * @param date
	 *            the date object to compare
	 * @return true, if successful
	 */
	@Override
	public boolean equals(Object date) {
		if (!(date instanceof Date))
			return false;

		Date d = (Date) date;

		if (d.getSchedule() != this.schedule)
			return false;

		if (Math.abs(d.getDate().getTime() - this.date.getTime()) > 3600000)
			return false;

		return true;
	}

	/**
	 * Clone a Date object
	 * 
	 */
	@Override
	public Object clone() {
		return new Date(this.date.getTime(), this.schedule);
	}

	/**
	 * Test if two dates are representing the same day
	 * 
	 * @param date
	 *            the date to compare with
	 * @return true, if successful
	 */
	public boolean dayEquals(Object date) {
		if (!(date instanceof Date))
			return false;

		Date d = (Date) date;

		if (Math.abs(d.getDate().getTime() - this.date.getTime()) > 3600000)
			return false;

		return true;
	}

	/**
	 * Before.
	 * 
	 * @param d
	 *            the date to compare with
	 * @return true, if successful
	 */
	public boolean before(Date d) {
		if (this.date.compareTo(d.date) < 0)
			return true;

		if (this.date.compareTo(d.date) == 0)
			return (this.schedule.ordinal() < d.schedule.ordinal());

		return false;
	}

	@Override
	public String toString() {
		DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL,
				Locale.getDefault());

		String strDate = "\"" + dateFormat.format(this.date)
				+ "\" (french date format)";
		return "the " + strDate + " at " + this.schedule.name();
	}

	public String toStringHtml() {
		DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL,
				Locale.getDefault());

		return (dateFormat.format(this.date));
	}

}
